#pragma once

#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#else
#include <pthread.h>
#endif

#include "common.hpp"

namespace base {

namespace eec {

class atomic_int {
public:
    atomic_int() : vl_(0) {}
    atomic_int(const int& _src) : vl_(_src) {}

public:
    int fetch_add(const int& _vl) {
#ifdef WIN32
        return InterlockedExchangeAdd(&vl_, _vl) + _vl;
#else
        return __sync_fetch_and_add(&vl_, _vl) + _vl;
#endif
    }
    int fetch_sub(const int& _vl) {
#ifdef WIN32
        return InterlockedExchangeAdd(&vl_, -_vl) - _vl;
#else
        return __sync_fetch_and_sub(&vl_, _vl) - _vl;
#endif
    }

    int load() const {
        atomic_int* l_ref = const_cast<atomic_int*>(this);
        return l_ref->fetch_add(0);
    }
    
    int exchange(const int& _vl) {
#ifdef WIN32
        return InterlockedExchange(&vl_, _vl);
#else
        return __sync_lock_test_and_set(&vl_, _vl);
#endif
    }
    void store(const int& _vl) {
        exchange(_vl);
    }
    
    int compare_and_swap(const int& _compare, const int& _swap) {
#ifdef WIN32
        return InterlockedCompareExchange(&vl_, _swap, _compare);
#else
        return __sync_val_compare_and_swap(&vl_, _compare, _swap);
#endif
    }

    bool not_compare_and_sub(const int& _compare, const int& _vl, int& _src) {
        int l_now = load();
        while (_compare != l_now) {
            int l_new = compare_and_swap(l_now, l_now - _vl);
            if (l_new == l_now) {
                _src = l_now;
                return true;
            }
        }
        return false;
    }
    bool not_compare_and_add(const int& _compare, const int& _vl, int& _src) {
        int l_now = load();
        while (_compare != l_now) {
            int l_new = compare_and_swap(l_now, l_now + _vl);
            if (l_new == l_now) {
                _src = l_now;
                return true;
            }
            l_now = l_new;
        }
        return false;
    }
    bool compare_and_add(const EEC_STD::function<bool(const int& _now)>& _compare, const int& _vl, int& _src) {
        int l_now = load();
        while (_compare(l_now)) {
            int l_new = compare_and_swap(l_now, l_now + _vl);
            if (l_new == l_now) {
                _src = l_now;
                return true;
            }
            l_now = l_new;
        }
        return false;
    }

private:
    atomic_int(const atomic_int&);
    atomic_int& operator=(const atomic_int&);

private:
#ifdef WIN32
    volatile long vl_;
#else
    volatile int vl_;
#endif
};

}   // namespace eec

}   // namespace base